<?php
/**
 * Used for ssl decryption
 */
namespace Cephalopod\SSL;

use Cephalopod\Tube;
use SplQueue;
use Exception;

class SSLTube implements Tube
{
    private SplQueue $queue;

    private string $privateKey;

    private string $buffer = '';

    public function __construct()
    {
        $this->queue = new SplQueue();
    }

    public function setPrivateKey(string $privateKey)
    {
        $this->privateKey = $privateKey;
    }

    public function dequeue(): string
    {
        $sslBlockSize = OpenSSL::KEY_BITS / 8; // byte
        $decryptedData = '';

        while (true) {
            while (strlen($this->buffer) < $sslBlockSize && !$this->queue->isEmpty()) {
                $this->buffer .= $this->queue->dequeue();
            }
            if (strlen($this->buffer) < $sslBlockSize) {
                break;
            }
            $sslBlock = substr($this->buffer, 0, $sslBlockSize);
            $this->buffer = substr($this->buffer, $sslBlockSize);
            if (false === $this->buffer) {
                $this->buffer = '';
            }
            try {
                $decryptedDataTemp = OpenSSL::decrypt($sslBlock, $this->privateKey);
            } catch (Exception $e) {
                throw new Exception('SSLTube->dequeue() fail, data=' . $this->readAbleBinrary($sslBlock) . '. ' . $e->getMessage());
            }
            if ('' == $decryptedDataTemp) {
                throw new Exception('SSLTube decryption failed, block size=' . strlen($sslBlock));
            }
            $decryptedData .= $decryptedDataTemp;
        }
        return $decryptedData;
    }

    public function enqueue(string $binraryData)
    {
        $this->queue->enqueue($binraryData);
    }

    private function readAbleBinrary(string $data): string
    {
        $baseChars = "0123456789abcdefghijklnmopqrstuvwxyzABCDEFGHIJKLNMOPQRSTUVWXYZ:-/\\?&!`~@#$%^*()_+=[]{}|;\"'<>,.";
        $len = strlen($data);
        $result = '';
        for ($i = 0; $i < $len; $i++) {
            $ch = substr($data, $i, 1);
            if (false === strpos($baseChars, $ch)) {
                $result .= '.';
            } else {
                $result .= $ch;
            }
        }
        return $result;
    }
}
